﻿using System;
using System.IO;
using DBFile.Core.Interfaces;

namespace DBFile.Core.Web
{
	/// <summary>
	/// This class is a wrapper around System.Web.HttpPostedFile.  Since System.Web.HttpPostedFile has read only
	/// properties, this class was created to perform the same thing that it does except allow read-write to the
	/// properties.
	/// </summary>
	public class HttpPostedFile : IDBFile, IDisposable
	{
		#region Member Objects

		private long _contentLength = 0;
		private string _fileName = string.Empty;
		private string _contentType = string.Empty;
		private Stream _inputStream = null;
		private byte[] _fileBytes = new byte[0];
		private int _byteCount = 0;
		#endregion

		#region Constructors and Destructor
		/// <summary>
		/// Default construtor taking no arguments.  This is used in the process of getting a file from the 
		/// database.
		/// </summary>
		public HttpPostedFile()
		{
			// no file, so lets get one
		}

		/// <summary>
		/// Constructor used when uploading a file and you have a System.Web.HttpPostedFile object
		/// </summary>
		/// <param name="file">File posted in the Request</param>
		public HttpPostedFile(System.Web.HttpPostedFile file)
		{
			// check to make sure the file is not null
			if (file == null)
				throw new NullReferenceException("File is null");

			Initialize(file);
		}

        public HttpPostedFile(IDBFile dbFile)
        {
            // check to make sure the file is not null
            if (dbFile == null)
                throw new NullReferenceException("File is null");

            Initialize(dbFile);
        }

		/// <summary>
		/// When garbage collected, this Finalize method will call Dispose()
		/// </summary>
		~HttpPostedFile()
		{
			Dispose(true);
		}

		#endregion

		#region Properties
		/// <summary>
		/// Gets the size in bytes of an uploaded file.
		/// </summary>
		public long ContentLength
		{
			get
			{
				return _contentLength;
			}
			set
			{
				_contentLength = value;
			}
		}
		/// <summary>
		/// Gets the name of the file.  current implementation only allows for a filename and extension
		/// </summary>
		public string FileName
		{
			get
			{
				return _fileName;
			}
			set
			{
				_fileName = value;
			}
		}
		/// <summary>
		/// Gets the MIME content type of a file sent by a client.
		/// </summary>
		public string ContentType
		{
			get
			{
				return _contentType;
			}
			set
			{
				_contentType = value;
			}
		}
		/// <summary>
		/// Gets a Stream object which points to an uploaded file to prepare for reading the contents of the file.
		/// </summary>
		public Stream InputStream
		{
			get
			{
				// handle the case if it is null, return an empty stream
				if (_inputStream == null)
					return new System.IO.MemoryStream();
				else
				{
					return _inputStream;
				}
			}
			set
			{
				_inputStream = value;
			}
		}

		/// <summary>
		/// Gets the byte count in the FileBytes property.  This property should be the same as ContentLength.
		/// </summary>
		public int ByteCount
		{
			get
			{
				if (_byteCount == 0)  // data might be in the inputstream
					return Convert.ToInt32(_contentLength);
				else
					return _byteCount;
			}
			set
			{
				_byteCount = value;
			}
		}

        byte[] IDBFile.FileBytes
        {
            get { return _fileBytes; }
            set 
            { 
                _fileBytes = value;
                _byteCount = (int)ContentLength;
                // Reset the stream
                Write(_fileBytes, 0, _byteCount);
            }
        }

		#endregion

		/// <summary>
		/// Writes a sequence of bytes to the current stream and advances the current position within this 
		/// stream by the number of bytes written.
		/// </summary>
		/// <param name="buffer">An array of bytes. This method copies count bytes from buffer to the current stream.</param>
		/// <param name="offset">The zero-based byte offset in buffer at which to begin copying bytes to the current stream.</param>
		/// <param name="count">The number of bytes to be written to the current stream.</param>
		public void Write(byte[] buffer, int offset, int count)
		{
			_inputStream = new MemoryStream(buffer, 0, count, false, false);
		}

		/// <summary>
		/// Reads a sequence of bytes from the current stream and advances the position within the stream by 
		/// the number of bytes read.
		/// </summary>
		/// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified 
		/// byte array with the values between offset and (offset + count- 1) replaced by the bytes read from the current source.</param>
		/// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the current stream. </param>
		/// <param name="count">The maximum number of bytes to be read from the current stream.</param>
		/// <returns></returns>
		public int Read(byte[] buffer, int offset, int count)
		{
			int bytesRead = _inputStream.Read(buffer, offset, count);
			return bytesRead;
		}
		/// <summary>
		/// Closes the underlying stream
		/// </summary>
		public void Close()
		{
			Dispose(true);
		}

		#region Utility Methods
		/// <summary>
		/// Method used to initialize the local variables when this object is being used in a file upload.
		/// </summary>
		/// <param name="file"></param>
		private void Initialize(System.Web.HttpPostedFile file)
		{
			_contentLength = file.ContentLength;
			_fileName = file.FileName;
			_contentType = file.ContentType;
			_inputStream = file.InputStream;

			_fileBytes = new byte[_contentLength];
			_byteCount = _inputStream.Read(_fileBytes, 0, file.ContentLength);
		}

        private void Initialize(IDBFile dbFile)
        {
            _contentLength = dbFile.ContentLength;
            _fileName = dbFile.FileName;
            _contentType = dbFile.ContentType;
            
            _fileBytes = new byte[_contentLength];
            _byteCount = _fileBytes.Length;
            Write(dbFile.FileBytes, 0, _byteCount);
        }

		#endregion

		#region IDisposable Members

		/// <summary>
		/// Cleans up by calling the close method.
		/// </summary>
		public void Dispose()
		{
			Dispose(true);
		}

		/// <summary>
		/// Releases unmanaged and - optionally - managed resources
		/// </summary>
		/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
		protected virtual void Dispose(bool disposing)
		{
			if (disposing)
			{
				if (_inputStream != null)
				{
					_inputStream.Close();
				}
			}
			GC.SuppressFinalize(this);
		}

		#endregion
	}
}
